﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml;
using System.IO;
using TeamFoundation.EventBus.Client.Events;

namespace TeamFoundation.EventBus.Client
{
    /// <summary>
    /// Represents event information about a TFS event.
    /// </summary>
    public class TfsEventArgs : EventArgs
    {
        private static readonly XNamespace _rootNamespace = Utilities.EventNamespace;

        /// <summary>
        /// Gets the event data for the event.
        /// </summary>
        public XElement EventData
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the event.
        /// </summary>
        public object Event
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the identity data for the event.
        /// </summary>
        public XElement IdentityData
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the TFS identity.
        /// </summary>
        public TFSIdentity Identity
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the subscription ID for the event.
        /// </summary>
        public int SubscriptionId
        {
            get;
            private set;
        }

        /// <summary>
        /// Creates a new instance of the <see cref="TfsEventArgs"/>
        /// class.
        /// </summary>
        /// <param name="eventData">The event data.</param>
        /// <param name="identityData">The identity data.</param>
        /// <param name="subscriptionId">The subscription id.</param>
        public TfsEventArgs(XElement eventData, XElement identityData, int subscriptionId)
        {
            EventData = eventData;
            IdentityData = identityData;
            SubscriptionId = subscriptionId;
            Event = SerializationManager.Instance.Deserialize(eventData) ?? eventData;
            Identity = SerializationManager.Instance.Deserialize<TFSIdentity>(identityData);
        }

        /// <summary>
        /// Parses a queue message to <see cref="TfsEventArgs"/>.
        /// </summary>
        /// <param name="reader">The queue data.</param>
        /// <param name="readerSettings">The XML reader settings to use.</param>
        /// <returns>A <see cref="TfsEventArgs"/> containing the information that was parsed.</returns>
        public static TfsEventArgs Parse(Stream reader, XmlReaderSettings readerSettings)
        {
            using (XmlReader xreader = XmlReader.Create(reader, readerSettings))
            {
                XDocument doc = XDocument.Load(xreader);
                var rootNode = doc.Elements(_rootNamespace.GetName("event"));

                return new TfsEventArgs(
                    rootNode.Elements(_rootNamespace.GetName("payload")).Elements().FirstOrDefault(),
                    rootNode.Elements(_rootNamespace.GetName("identity")).Elements().FirstOrDefault(),
                    rootNode.Attributes("subid").Select(x => NoFailParse(x.Value)).FirstOrDefault());
            }
        }

        private static int NoFailParse(string message)
        {
            int result;
            if (!int.TryParse(message, out result))
                return -1;
            else
                return result;
        }


        /// <summary>
        /// Determines if the event is a certain type.
        /// </summary>
        /// <typeparam name="T">The type to check.</typeparam>
        /// <returns>Whether the event is a certain type.</returns>
        public bool Is<T>()
        {
            return Event is T;
        }

        /// <summary>
        /// Gets the <see cref="Event"/> as a strongly typed instance.
        /// </summary>
        /// <typeparam name="T">The type to cast <see cref="Event"/> to.</typeparam>
        /// <returns>An instance of <typeparamref name="T"/>.</returns>
        public T As<T>()
        {
            return (T)Event;
        }
    }
}
